/*
 * A facade to adapt the mpi api
 */
#ifndef MPI_FACADE_H
#define MPI_FACADE_H

#include <mpi.h>

namespace ibm
{

struct MpiFacade
{
	template<typename T>
	static void allReduce(T sentVal, T& recvVal, MPI_Op op, MPI_Comm comm = MPI_COMM_WORLD );

	/// added by Lipen 2008/07/01
	/// Element-per-element all-reduction of a vector of data
	template<typename T>
	static void allReduceVect(std::vector<T>& sendVal, std::vector<T>& recvVal, MPI_Op op, MPI_Comm comm = MPI_COMM_WORLD);

	template<typename T>
	static void allReduceVec(T* sendVal, T* recvVal, int count, MPI_Op op, MPI_Comm comm = MPI_COMM_WORLD);
};


template <>
void MpiFacade::allReduce<char>(char sendVal, char& recvVal, MPI_Op op, MPI_Comm comm )
{
	MPI_Allreduce(static_cast<void*>(&sendVal),
			static_cast<void*>(&recvVal), 1, MPI_CHAR, op, comm);
}

template <>
void MpiFacade::allReduce<int>(int sendVal, int& recvVal, MPI_Op op, MPI_Comm comm)
{
	MPI_Allreduce(static_cast<void*>(&sendVal),
			static_cast<void*>(&recvVal), 1, MPI_INT, op, comm);
}

template <>
void MpiFacade::allReduce<float>(float sendVal, float& recvVal, MPI_Op op, MPI_Comm comm)
{
	MPI_Allreduce(static_cast<void*>(&sendVal),
			static_cast<void*>(&recvVal), 1, MPI_FLOAT, op, comm);
}

template <>
void MpiFacade::allReduce<double>(double sendVal, double& recvVal, MPI_Op op, MPI_Comm comm)
{
	MPI_Allreduce(static_cast<void*>(&sendVal),
			static_cast<void*>(&recvVal), 1, MPI_DOUBLE, op, comm);
}


template<>
void MpiFacade::allReduceVect<char>(std::vector<char>& sendVal, std::vector<char>& recvVal, MPI_Op op, MPI_Comm comm)
{
	MPI_Allreduce(static_cast<void*>(&(sendVal[0])),
			static_cast<void*>(&(recvVal[0])),
			sendVal.size(),
			MPI_CHAR, op, comm);
}

template<>
void MpiFacade::allReduceVect<int>(std::vector<int>& sendVal, std::vector<int>& recvVal, MPI_Op op, MPI_Comm comm)
{
	MPI_Allreduce(static_cast<void*>(&(sendVal[0])),
			static_cast<void*>(&(recvVal[0])),
			sendVal.size(),
			MPI_INT, op, comm);
}

template<>
void MpiFacade::allReduceVect<float>(std::vector<float>& sendVal, std::vector<float>& recvVal, MPI_Op op, MPI_Comm comm)
{
	MPI_Allreduce(static_cast<void*>(&(sendVal[0])),
			static_cast<void*>(&(recvVal[0])),
			sendVal.size(),
			MPI_FLOAT, op, comm);
}

template<>
void MpiFacade::allReduceVect<double>(std::vector<double>& sendVal, std::vector<double>& recvVal, MPI_Op op, MPI_Comm comm)
{
	MPI_Allreduce(static_cast<void*>(&(sendVal[0])),
			static_cast<void*>(&(recvVal[0])),
			sendVal.size(),
			MPI_DOUBLE, op, comm);
}


template <>
void MpiFacade::allReduceVec<char>(char* sendVal, char* recvVal, int count, MPI_Op op, MPI_Comm comm)
{
	MPI_Allreduce(static_cast<void*>(sendVal), static_cast<void*>(recvVal), count, MPI_CHAR, op, comm);
}

template <>
void MpiFacade::allReduceVec<int>(int* sendVal, int* recvVal, int count, MPI_Op op, MPI_Comm comm)
{
	MPI_Allreduce(static_cast<void*>(sendVal), static_cast<void*>(recvVal), count, MPI_INT, op, comm);
}

template <>
void MpiFacade::allReduceVec<float>(float* sendVal, float* recvVal, int count, MPI_Op op, MPI_Comm comm)
{
	MPI_Allreduce(static_cast<void*>(sendVal), static_cast<void*>(recvVal), count, MPI_FLOAT, op, comm);
}

template <>
void MpiFacade::allReduceVec<double>(double* sendVal, double* recvVal, int count, MPI_Op op, MPI_Comm comm)
{
	MPI_Allreduce(static_cast<void*>(sendVal), static_cast<void*>(recvVal), count, MPI_FLOAT, op, comm);
}

} // namespace ibm
#endif // MPI_FACADE_H
